package com.cyy.springbootinit.service.impl;
import java.util.*;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cyy.springbootinit.common.ErrorCode;
import com.cyy.springbootinit.constant.CommonConstant;
import com.cyy.springbootinit.exception.ThrowUtils;
import com.cyy.springbootinit.mapper.EquipmentDataMapper;
import com.cyy.springbootinit.model.dto.equipmentdata.EquipmentDataQueryRequest;
import com.cyy.springbootinit.model.dto.message.PaymentMessage;
import com.cyy.springbootinit.model.entity.*;
import com.cyy.springbootinit.model.enums.EquipmentTypeConst;
import com.cyy.springbootinit.model.vo.EquipmentDataVO;
import com.cyy.springbootinit.model.vo.EquipmentInfoAllVO;
import com.cyy.springbootinit.model.vo.EquipmentInfoVO;
import com.cyy.springbootinit.service.*;
import com.cyy.springbootinit.service.impl.PaymentBillServiceImpl;
import com.cyy.springbootinit.service.impl.WaterFeeRuleServiceImpl;
import com.cyy.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 设备数据表服务实现
 *
 * @author 程序员cyy</a>
 * @from cyy</a>
 */
@Service
@Slf4j
public class EquipmentDataServiceImpl extends ServiceImpl<EquipmentDataMapper, EquipmentData> implements EquipmentDataService {

    @Resource
    private BalanceWarningService balanceWarningService;
    @Resource
    private BalanceAlarmService balanceAlarmService;
    @Resource
    private PaymentBillServiceImpl paymentBillService;
    @Resource
    private EleFeeRuleService eleFeeRuleService;
    @Resource
    private WaterFeeRuleServiceImpl waterFeeRuleService;
    @Resource
    private UserService userService;
    @Resource
    private EquipmentInfoService equipmentInfoService;
    @Resource
    private ReadingHistoryService readingHistoryService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 校验数据
     *
     * @param equipmentData
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validEquipmentData(EquipmentData equipmentData, boolean add) {


        ThrowUtils.throwIf(equipmentData == null, ErrorCode.PARAMS_ERROR);
        // todo 从对象中取值
        Integer equipmentId = equipmentData.getEquipmentId();
        Double readValue = equipmentData.getReadValue();
        Double equipmentBalance = equipmentData.getEquipmentBalance();
        // 创建数据时，参数不能为空
        if (add) {
            // todo 补充校验规则
            ThrowUtils.throwIf(equipmentId ==null, ErrorCode.PARAMS_ERROR);
            ThrowUtils.throwIf(readValue ==null, ErrorCode.PARAMS_ERROR);
            ThrowUtils.throwIf(equipmentBalance ==null, ErrorCode.PARAMS_ERROR);
        }
        // 修改数据时，有参数则校验
        // todo 补充校验规则
    }

    /**
     * 获取查询条件
     *
     * @param equipmentDataQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<EquipmentData> getQueryWrapper(EquipmentDataQueryRequest equipmentDataQueryRequest) {
        QueryWrapper<EquipmentData> queryWrapper = new QueryWrapper<>();
        if (equipmentDataQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Integer dataId = equipmentDataQueryRequest.getDataId();
        Integer equipmentId = equipmentDataQueryRequest.getEquipmentId();
        Double readValue = equipmentDataQueryRequest.getReadValue();
        Double equipmentBalance = equipmentDataQueryRequest.getEquipmentBalance();
        String searchText = equipmentDataQueryRequest.getSearchText();
        String sortField = equipmentDataQueryRequest.getSortField();
        String sortOrder = equipmentDataQueryRequest.getSortOrder();
        // todo 补充需要的查询条件
        // 从多字段中搜索
        if (StringUtils.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        }
        // 模糊查询
        // JSON 数组查询
        // 精确查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(dataId), "dataId", dataId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(equipmentId), "equipmentId", equipmentId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(readValue), "readValue", readValue);
        queryWrapper.eq(ObjectUtils.isNotEmpty(equipmentBalance), "equipmentBalance", equipmentBalance);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取设备数据表封装
     *
     * @param equipmentData
     * @param request
     * @return
     */
    @Override
    public EquipmentDataVO getEquipmentDataVO(EquipmentData equipmentData, HttpServletRequest request) {
        // 对象转封装类
        EquipmentDataVO equipmentDataVO = EquipmentDataVO.objToVo(equipmentData);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Integer equipmentId = equipmentDataVO.getEquipmentId();
        QueryWrapper<EquipmentInfo> equipmentInfoQueryWrapper = new QueryWrapper<>();
        equipmentInfoQueryWrapper.eq("equipmentId", equipmentId);
        EquipmentInfo equipmentInfo = equipmentInfoService.getOne(equipmentInfoQueryWrapper);
        equipmentDataVO.setEquipmentInfoVO(EquipmentInfoVO.objToVo(equipmentInfo));
        return equipmentDataVO;
    }

    /**
     * 分页获取设备数据表封装
     *
     * @param equipmentDataPage
     * @param request
     * @return
     */
    @Override
    public Page<EquipmentDataVO> getEquipmentDataVOPage(Page<EquipmentData> equipmentDataPage, HttpServletRequest request) {
        List<EquipmentData> equipmentDataList = equipmentDataPage.getRecords();
        Page<EquipmentDataVO> equipmentDataVOPage = new Page<>(equipmentDataPage.getCurrent(), equipmentDataPage.getSize(), equipmentDataPage.getTotal());
        if (CollUtil.isEmpty(equipmentDataList)) {
            return equipmentDataVOPage;
        }
        // 对象列表 => 封装对象列表
        List<EquipmentDataVO> equipmentDataVOList = equipmentDataList.stream().map(equipmentData -> {
            return EquipmentDataVO.objToVo(equipmentData);
        }).collect(Collectors.toList());

        // 填充信息
        equipmentDataVOList.forEach(equipmentDataVO -> {
            Integer equipmentId = equipmentDataVO.getEquipmentId();
            QueryWrapper<EquipmentInfo> equipmentInfoQueryWrapper = new QueryWrapper<>();
            equipmentInfoQueryWrapper.eq("equipmentId", equipmentId);
            EquipmentInfo equipmentInfo = equipmentInfoService.getOne(equipmentInfoQueryWrapper);
            equipmentDataVO.setEquipmentInfoVO(EquipmentInfoVO.objToVo(equipmentInfo));
        });
        // endregion

        equipmentDataVOPage.setRecords(equipmentDataVOList);
        return equipmentDataVOPage;
    }

    @Override
    public List<EquipmentDataVO> getEquipmentDataVoByDormitoryId(Integer dormitoryId) {
        // 根据宿舍号获取设备信息
        QueryWrapper<EquipmentInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("dormitoryId",dormitoryId);
        List<EquipmentInfo> equipmentInfos = equipmentInfoService.list(queryWrapper);
        List<Integer> equipmentIds = equipmentInfos.stream().map(EquipmentInfo::getEquipmentId).collect(Collectors.toList());
        //定位水表电表id保证返回数据一致性
        int water=0,ele=0;
        //判断java字符串应该用的是equals
        for (EquipmentInfo equipmentInfo : equipmentInfos) {
            System.out.println("equipmentInfo.getTypeName()"+equipmentInfo.getTypeName());
            System.out.println("EquipmentName.WaterEquipment"+ EquipmentTypeConst.WaterEquipment.getValue());
            System.out.println("EquipmentName.ElectricityEquipment"+ EquipmentTypeConst.ElectricityEquipment.getValue());
            System.out.println(Objects.equals(equipmentInfo.getTypeName(),EquipmentTypeConst.WaterEquipment.getValue()));
            if(Objects.equals(equipmentInfo.getTypeName(), EquipmentTypeConst.WaterEquipment.getValue())) {
                water=equipmentInfo.getEquipmentId();
                System.out.println("equipmentInfo.getEquipmentId()"+equipmentInfo.getEquipmentId());
                System.out.println("water"+water);
            }
            else if(Objects.equals(equipmentInfo.getTypeName(), EquipmentTypeConst.ElectricityEquipment.getValue()))
                ele=equipmentInfo.getEquipmentId();
        }
        //获取当前月设备开始数据
        QueryWrapper<EquipmentData> queryWrapper1=new QueryWrapper<>();
        queryWrapper1.in("equipmentId",equipmentIds);
        List<EquipmentData> equipmentDataS = list(queryWrapper1);
        //或缺当前抄表数据
        List<ReadingHistory> readingHistories = readingHistoryService.TwoLastData(equipmentIds);
        //把数据按照水表电表顺序返回
        List<EquipmentDataVO> ans=new ArrayList<>();
        System.out.println("你好");
        System.out.println(water+" "+ele);
        EquipmentDataVO one=new EquipmentDataVO();
        EquipmentDataVO two=new EquipmentDataVO();
        for(EquipmentData equipmentData:equipmentDataS){
            for(ReadingHistory readingHistory:readingHistories){
            int equipmentId = equipmentData.getEquipmentId();
                if(equipmentId==readingHistory.getEquipmentId()&&equipmentId==water) {
                    one=getEquipmentDataVO(equipmentData,null);
                    System.out.println("Id");
                    System.out.println(one.getEquipmentId());
                    one.setCurrentReadValue(readingHistory.getReadingValue());
                    Double totalAmount = waterFeeRuleService.calculateOrderFee(readingHistory.getReadingValue() - equipmentData.getReadValue()).getTotalAmount();
                    one.setTotalCost(totalAmount);
                    System.out.println("totalAmount"+totalAmount);
                }
                else if(equipmentId==readingHistory.getEquipmentId()&&equipmentId==ele){
                    two=getEquipmentDataVO(equipmentData,null);

                    two.setCurrentReadValue(readingHistory.getReadingValue());
                    Double totalAmount = eleFeeRuleService.calculateOrderFee(readingHistory.getReadingValue() - equipmentData.getReadValue()).getTotalAmount();
                    two.setTotalCost(totalAmount);
                }
            }
        }
        ans.add(one); ans.add(two);
        return ans;
    }
    @Transactional
    @Override
    public Boolean pay(Double money, Integer equipmentId, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        /*User loginUser =new User();
        loginUser.setMoney(1000000000.0);
        loginUser.setId(2L);
        loginUser.setDormitoryId(1);*/
        Double money1 = loginUser.getMoney();
        if(money1<money){
            ThrowUtils.throwIf(money1<money,ErrorCode.PARAMS_ERROR,"余额不足");
        }
        RLock lock = redissonClient.getLock("pay" + equipmentId);
        try{
            boolean b = lock.tryLock(10, 10, TimeUnit.SECONDS);
            if(b){
                //修改设备余额
                QueryWrapper<EquipmentData> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("equipmentId",equipmentId);
                EquipmentData equipmentData = getOne(queryWrapper);
                equipmentData.setEquipmentBalance(equipmentData.getEquipmentBalance()+money);
                updateById(equipmentData);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            lock.unlock();
        }
        //修改个人余额
        loginUser.setMoney(money1-money);
        userService.updateById(loginUser);

        // 将后续处理放入消息队列
        PaymentMessage message = new PaymentMessage();
        message.setMoney(money);
        message.setEquipmentId(equipmentId);
        message.setUserId(loginUser.getId());
        message.setDormitoryId(loginUser.getDormitoryId());

        // 发送消息到队列
        rabbitTemplate.convertAndSend("payment.exchange", "payment.routing.key", message);

        return true;
    }
    @Override
    public List<EquipmentInfoAllVO> getEquipmentInfoAll() {
        return null;
    }

}
